package com.wangshili.common.aspect;

import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Date;

import org.apache.shiro.SecurityUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.wangshili.common.Utils.IPUtils;
import com.wangshili.common.annotation.RequiredLog;
import com.wangshili.common.exception.ServiceException;
import com.wangshili.pojo.sys.entity.SysLog;
import com.wangshili.pojo.sys.entity.SysUser;
import com.wangshili.sys.service.SysLogService;

/**
 * 日志切面，记录操作信息
 * @author wangshili
 */
@Aspect
@Component
public class SysLogAspect {
	private static final Logger log = 
			LoggerFactory.getLogger(SysLogAspect.class);
	@Autowired
	private SysLogService sysLogService;
	
	@Pointcut("@annotation(com.wangshili.common.annotation.RequiredLog)")
	public void doRequiredLog() {}
	
	@Around("doRequiredLog()")
	public Object around(ProceedingJoinPoint jc) throws Throwable {

		Long startTime = System.currentTimeMillis();
		Object proceed = jc.proceed();//执行方法
		Long endTime = System.currentTimeMillis();
		Long totalTime = endTime-startTime;
		log.debug("执行时间："+new Date(totalTime));
		saveSysLog(jc,totalTime);
		return proceed;	
	}
	
	/**
	 * 获取执行人员名称，操作动作，执行方法全路径，请求参数，执行时间，ip段，
	 * @param jc
	 * @param totalTime
	 * @throws JsonProcessingException 
	 * @throws SecurityException 
	 * @throws NoSuchMethodException 
	 */
	private void saveSysLog(ProceedingJoinPoint jc, Long totalTime) 
			throws JsonProcessingException, NoSuchMethodException, SecurityException {
		//1.获取日志信息，signature签名，这句不太理解
		MethodSignature ms = 
				(MethodSignature)jc.getSignature();
		//2.获取该方法的类的字节码对象
		Class<? extends Object> jcClass = jc
											.getTarget() //获取该方法的类对象
											.getClass();	//获取字节码对象
		//获取类路径名称
		String className = jcClass.getName();//获取类路径名
		
		//获取方法名
//		String methodName = ms.getMethod().getName();
		
		
		//获取全部参数类型
//		Class[] parameterTypes = ms.getParameterTypes();
		
		//根据方法名和参数获取目标对象方法（获取AOP的方法可能还需要更加不同的版本区分）
		//方法1
//		Method targetMethod = 
//				jcClass.getDeclaredMethod(methodName, parameterTypes);
		//方法2
		Method method = ms.getMethod();
		
		//获取用户名
		SysUser user = (SysUser)SecurityUtils.getSubject().getPrincipal();
		
		//获取请求的参数
		Object[] args = jc.getArgs();
		
		//将参数转为字符串
		//方法1
//		String params = 
//				new ObjectMapper().writeValueAsString(args);
		//方法2
		String params = Arrays.toString(args);	
		
		//封装日志信息
		SysLog sysLog = new SysLog();
		
		//老师笔记：假如目标方法对象上有注解，获取注解定义的操作值
		//获取该方法上指定的注解的值
		//方法1，配合上面的方法1
//		RequiredLog requiredLog = 
//				targetMethod
//				.getDeclaredAnnotation(RequiredLog.class);
//		if(requiredLog!=null) {
//			//取注解上的值
//			sysLog.setOperation(requiredLog.value());
//		}
		//方法2，为了程序的复用时的健壮性，这个获取注解最好判断下
		RequiredLog annotation = method.getAnnotation(RequiredLog.class);
		if(annotation!=null)
			sysLog.setOperation(annotation.value());
		//设置用户名
		sysLog.setUsername(user.getUsername());
		//设置参数
		sysLog.setParams(params);
		//设置类路径名+方法名
		sysLog.setMethod(className+"."+method.getName());
		//设置Ip,直接使用一个类
		sysLog.setIp(IPUtils.getIpAddr());
		//设置时间
		sysLog.setTime(totalTime);
		//设置创建时间
		sysLog.setCreateDate(new Date());
		
		int rows = sysLogService.insertSysLog(sysLog);
		if(rows<1)
			throw new ServiceException("日志保存失败");
	}
}
